	.code16
# rewrite with AT&T syntax and add comments by xuexuan
# AT&T syntax :https://gist.github.com/mishurov/6bcf04df329973c15044
# CF 和 OF 的区别  https://zhuanlan.zhihu.com/p/480278568
# SYS_SIZE is the number of clicks (16 bytes) to be loaded.
# 0x3000 is 0x30000 bytes = 196kB, more than enough for current
# versions of linux
#
	.equ SYSSIZE, 0x3000
#
#	bootsect.s		(C) 1991 Linus Torvalds
#
# bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
# iself out of the way to address 0x90000, and jumps there.
#
# It then loads 'setup' directly after itself (0x90200), and the system
# at 0x10000, using BIOS interrupts. 
#
# NOTE# currently system is at most 8*65536 bytes long. This should be no
# problem, even in the future. I want to keep it simple. This 512 kB
# kernel size should be enough, especially as this doesn't contain the
# buffer cache as in minix
#


# 启动过程bootsect.s的内存布局，我使用的模拟器是16M的内存

# |--------------|0x1000000(16M)
# | ROM BIOS     |
# |--------------|
# |              |
# |              |
# |              |
# |   暂时未用    |
# |              |
# |              |
# |              |
# |              |
# |              |
# |              |
# |--------------|0x100000(1M)
# | ROM BIOS映射区|
# |--------------|0xF0000(960K)
# | 其他BIOS映射区 |
# |--------------|0xE0000(896K)
# |其他卡ROM BIOS |
# |--------------|0xC7FF(511K)
# | VGA ROM BIOS |
# |--------------|0xC0000(768K)
# |   显示缓冲区   |
# |--------------|0xA0000(640K)
# |              |
# |--------------|0x9FF00(639.75K)
# |    栈空间     |
# |--------------|0x90A00(578.5K)
# | setup.s      |
# |--------------|0x90200(576.5K)
# | bootsect.s   |
# |--------------|0x90000(576K)
# |              |
# |              |
# |  为system    |
# |  预留的空间    |
# |              |
# |--------------|0x40000
# |              |
# |              |
# |              |
# |              |
# |    system    |
# |              |
# |              |
# |              |
# |              |
# |              |
# |--------------|0x10000(64K)
# |              |
# |--------------|
# |  bootsect.s  |
# |--------------|0x7C00(31K)
# |              |
# |              |
# |              |
# |              |
# |              |
# |              |
# |              |
# |              |
# |              |
# |--------------|0x500(1280)
# |   BIOS数据区  |
# |--------------|0x400(1024)
# |   中断向量表   |
# |--------------|0x0(0)


#
# Linux 操作系统启动流程:
# 1、当 PC 的电源打开后，默认是实模式，加载BIOS程序到物理地址空间的顶部， CPU会自动把代码段寄存器CS设置为OxF000,IP设置为 OxFFF0, 也就是物理内存的距离最顶部的16字节处，
#    然后执行这里的指令，这里的指令是一条跳转指令，跳转到BIOS代码中 64KB 范围内的某一条指令开始执行。
#
# 2、BIOS 在执行了一些硬件检测和初始化操作之后，在物理内存0开始处，初始化中断向量表，大小为0x400 字节(1KB)，这些向量表会在setup.s中使用
#
# 3、就会把与原来 PC 机兼容的 64KB BIOS代码和数据复制到内存低端 1M 末端的64K 处(为了兼容之前的PC的软件，1MB以下的物理内存分配与原来的PC保持一致)，
#    然后跳转到这个地方并且让 CPU 进入真正的实地址模式工作，
#
# 4、最后 BIOS 就会从 硬盘或其他块设备把操作系统引导程序(bootsect.s 512Byte)加载到内存 [0x07c00, 0x07e00)，

# 5、检查到0x07dfe和0x07dff两个字节为0xAA55时，跳转到0x07c00继续执行bootsect.s。就是本程序的_start处
#
#
# bootsect.s的主要任务：
# 1、将[0x07c00, 0x07e00)内存段的内容复制到[0x90000, 0x90200)内存段中, #
#    然后跳转到0x9000段中执行bootsect.s在[0x07c00, 0x07e00)中未被执行的后续代码。#
# 2、 将启动盘上的setup.s程序读取到[0x90200, 0x90A00)内存段中; #
# 3、 将启动盘上的system操作系统程序拷贝到[0x10000, 0x40000)内存段中。#
#

# The loader has been made as simple as possible, and continuos
# read errors will result in a unbreakable loop. Reboot by hand. It
# loads pretty fast by getting whole sectors at a time whenever possible.

	.global _start, begtext, begdata, begbss, endtext, enddata, endbss
	.text
	begtext:
	.data
	begdata:
	.bss
	begbss:
	.text

	.equ SETUPLEN, 4		# nr of setup-sectors
	.equ BOOTSEG, 0x07c0		# original address of boot-sector
	.equ INITSEG, 0x9000		# we move boot here - out of the way
	.equ SETUPSEG, 0x9020		# setup starts here
	.equ SYSSEG, 0x1000		# system loaded at 0x10000 (65536).
	.equ ENDSEG, SYSSEG + SYSSIZE	# where to stop loading

# ROOT_DEV:	0x000 - same type of floppy as boot.
#		0x301 - first partition on first drive etc
#
# 设置默认的根文件系统设备(分区), 它被存储在bootsect.s的508偏移处。
# 可了解设备驱动程序如kernel/blk_drv/hd.c(sys_setup)后再回头理解此处程序。

# 设备号 0x301 指定根文件系统设备是第1个硬盘的第1个分区。
# 在编译这个内核时你可以根据自己根文件 系统所在设备位置修改这个设备号。
# 这个设备号是 Linux 系统老式的硬盘设备号命名方式，硬盘设备号具体值的含义如下：
# 设备号=主设备号*256+次设备号（也即 dev_no = (major<(8) + minor)
# 主设备号：1-内存，2-磁盘，3-硬盘，4-ttyx, 5-tty, 6-并行口，7-非命名管道）
# 0x300 - /dev/hdO - 代表整个第1个硬盘；
# 0x301 - /dey/hd1 - 第1个盘的第1个分区；
# ...
# 0x304 - /dev/hd4 - 第1个盘的第4个分区；
# 0x305 - /dev/hd5 - 代表整个第2个硬盘；
# 0x306 - /dev/hd6 - 第2个盘的第1个分区；
# ...
# 0x309 - /dev/hd9 - 第2个盘的第4个分区；

	.equ ROOT_DEV, 0x301 #源码是0x306
	.equ SWAP_DEV, 0x304
	ljmp $BOOTSEG, $_start
_start:                 #break *0x7c00 of gdb is here
	mov	$BOOTSEG, %ax	#将ds段寄存器设置为0x7C0
	mov	%ax, %ds
	mov	$INITSEG, %ax	#将es段寄存器设置为0x900
	mov	%ax, %es
	mov	$256, %cx		#设置移动256次，每次一个字，共512字节
	sub	%si, %si		#源地址	ds:si = 0x07C0:0x0000
	sub	%di, %di		#目标地址 es:di = 0x9000:0x0000
	rep					#重复执行并递减cx的值
	movsw				#从内存[si]处移动cx个字到[di]处

    # 完成引导程序的拷贝后, 跳转到 0x9000:go处，执行后续命令
	ljmp	$INITSEG, $go
	# 长跳转后，代码段寄存器 CS = 0x9000, IP = 0x7c00
go:	mov	%cs, %ax            #将ds，es，ss都设置成移动后代码所在的段处(0x9000)
	mov	%ax, %ds
	mov	%ax, %es

	mov	$0xfef4, %dx		# arbitrary value >>512 - disk parm size

    # put stack at 0x9ff00.
    # 实模式栈内存的起始地址，ss:sp = 0x9000:0xff00,
	mov	%ax, %ss		# put stack at 0x9ff00 - 12.
	mov	%dx, %sp
/*
 *	Many BIOS's default disk parameter tables will not
 *	recognize multi-sector reads beyond the maximum sector number
 *	specified in the default diskette parameter tables - this may
 *	mean 7 sectors in some cases.
 *
 *	Since single sector reads are slow and out of the question,
 *	we must take care of this by creating new parameter tables
 *	(for the first disk) in RAM.  We will set the maximum sector
 *	count to 18 - the most we will encounter on an HD 1.44.
 *
 *	High doesn't hurt.  Low does.
 *
 *	Segments are as follows: ds=es=ss=cs - INITSEG,
 *		fs = 0, gs = parameter table segment
 */
# 1、从内存0x0000:0x0078处复制原软驱参数表到0x9000:0xfef4处。表长12字节。
# 2、修改表中偏移4处的每磁道最大扇区数为18。
    push $0x0
    pop %fs             # This pops the top value from the stack (which we just pushed, so it's 0) into the fs segment register.
	mov	$0x78, %bx		# fs:bx is parameter table address
	#seg fs
    lgs %fs:(%bx),%si   # gs:si is source


	mov	%dx, %di		# es:di is destination
	mov	$6, %cx			# copy 12 bytes
	cld

	rep  movsb %fs:(%si), %es:(%di)

	mov	%dx, %di
	movb $18, 4(%di)		# patch sector count

	mov	%di, %fs:(%bx)
	mov	%es, %fs:2(%bx)

	mov	%cs, %ax
	mov	%ax, %fs
	mov	%ax, %gs

	xorb	%ah, %ah		# reset FDC
	xorb	%dl, %dl
	int	$0x13


# load the setup-sectors directly after the bootblock.
# Note that 'es' is already set up.
# 在完成数据和堆栈寄存器的设置后,
# 接下来读取位于启动盘(软盘)中[1..5]扇区的setup.s程序到[0x90200, 0x90a00)内存段,
# 若读取失败则重置软盘再读, 直到读取成功为止。

# 查BIOS 中断手册。http://staff.ustc.edu.cn/~hufy/Microcomputer/%B8%BD%C2%BC/BIOS_DOS%D6%D0%B6%CF%B9%A6%C4%DC%B5%F7%D3%C3%B4%F3%C8%AB.pdf
# 根据文档中的：直接磁盘服务(Direct Disk Service——INT 13H) ，读取磁盘的格式如下：
# ---入参---。
# ah = 02h, 调用BIOS 13h中(子程序号为02h)的读磁盘子程序;
# al = 欲读扇区数, SETUPLEN=4;
# cx = 0x0002, 从柱面0的第2扇区开始读。
# ch=磁道(柱面)号的低八位   cl＝开始扇区(位0-5),磁道号高2位(位6－7)
# |F|E|D|C|B|A|9|8|7|6|5-0|
# | | | | | | | | | |   `----- 欲读起始扇区
# | | | | | | | | `---------   欲读起始柱面高2位
# `------------------------    欲读起始柱面低8位
# dh = 磁头号, 0x00; dl = 驱动号, 0x00为软盘驱动;
# es:bx = 存储所读内容的内存首地址, 0x9000:200;

# ---出参和 标志寄存器的置位---
# ah = 状态号(00h-无错误..., );
# al = 成功读取扇区数;
# 标志寄存器CF=0, 读取成功; CF=1, 读取失败。
#
# 读取setup.s成功后, 通过BIOS 13h获取软盘参数,
# 将磁道(柱面)扇区数存储在cs(0x9000):sectors处,
# 供bootsect.s读取操作系统程序使用。
#
load_setup:
	xor	%dx, %dx		# drive 0, head 0
	mov	$0x0002, %cx		# sector 2, track 0
	mov	$0x0200, %bx		# address = 512, in INITSEG
	mov	$(0x0200 + SETUPLEN), %ax	# service 2, nr of sectors
	int	$0x13			    # read it 读取setup.s 程序
	jnc	ok_load_setup		# ok - continue  break *0x90036 of gdb is here. If the carry flag (CF) is not set, jump to the ok_load_setup label.

	push	%ax			# dump error code
	call	print_nl
	mov	%sp, %bp
	call	print_hex
	pop	%ax

	xorb	%dl, %dl			# reset FDC
	xorb	%ah, %ah
	int	$0x13
	jmp	load_setup





#功能描述：读取驱动器参数
#入口参数：
#AH＝08H
#DL＝驱动器，00H~7FH：软盘；80H~0FFH：硬盘
#出口参数：
#CF＝1——操作失败，
#AH＝状态代码，参见BIOS 中断手册, 33页的，功能号 01H 中的说明
#若读取成功，BL = 01H — 360K / 02H — 1.2M / 03H — 720K / 04H — 1.44M
#ch=磁道(柱面)号的低八位   cl＝扇区数(位0-5),磁道号高2位(位6－7)
#DH＝磁头数 DL＝驱动器数
#
#ES:DI＝磁盘驱动器参数表地址
ok_load_setup:

# Get disk drive parameters, specifically nr of sectors/track

	xorb %dl, %dl
	mov	$0x08, %ah		# AH=8 is get drive parameters
	int	$0x13
	xorb %ch, %ch
	#seg cs
	mov	%cx, %cs:sectors+0	# 把扇区数保存在%cs:sectors,cs是0x9000,sectors是当前程序的中标号
	mov	$INITSEG, %ax
	mov	%ax, %es

# Print some inane message
# 下面利用BIOS INT 0x10功能0x03和0x13来显示信息：msg1变量的字符，显示包括 回车和换行控制字符在内共28个字符。
# BIOS中断0x10功能号ah = 0x03，读光标位置。
# 输入：bh = 页号
# 返回：ch = 扫描开始线；cl = 扫描结束线；dh = 行号(0x00顶端)；dl = 列号(0x00最左边)。
#
# BIOS中断0x10功能号 ah = 0x13，显示字符串。
# 输入：
# al = 放置光标的方式及规定属性。0x01-表示使用bl中的属性值，光标停在字符串结尾处。
# bh = 显示页面号；
# bl = 字符属性；
# dh = 行号；dl = 列号。
# cx = 显示的字符串字符数。
# es:bp 此寄存器对指向要显示的字符串起始位置处。
	mov	$0x03, %ah		# read cursor pos
	xor	%bh, %bh
	int	$0x10
	
	mov	$0x1C, %cx
	mov	$0x0007, %bx		# page 0, attribute 7 (normal)
	#lea msg1, %bp
	mov $msg1, %bp
	mov	$0x1301, %ax		# write string, move cursor
	int	$0x10

# ok, we've written the message, now
# we want to load the system (at 0x10000)

	mov	$SYSSEG, %ax    # 读取操作系统程序到始于0x10000($SYSSEG)的内存段中, 共读0x30000字节
	mov	%ax, %es		# segment of 0x010000
	call	read_it
	call	kill_motor
    call	print_nl

# After that we check which root-device to use. If the device is
# defined (#= 0), nothing is done and the given device is used.
# Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
# on the number of sectors that the BIOS reports currently.

	#seg cs
# 检查要使用哪个根文件系统设备（简称根设备）。内存 cs:root_dev+0 != 0，说明有自定义的根文件系统设备，执行 root_defined->setup.s
# 否则根据 BIOS 报告的每磁道扇区数来确定使用/dev/PS0（2，28）或 /dev/at0 (2, 8)。
#
# 上面一行中两个设备文件的含义：
# 在Linux 中软驱的
# 主设备号是2(参见第43 行的注释)，
# 次设备号 = type*4 + nr，其中nr为0-3分别对应软驱 A、B、C或D；type 是软驱的类型（2->1.2MB或 7->1.44MB等）。
#
# 因为 7*4+0=28，所以 /dev/PS0 (2, 28)指的是1.44MB A 驱动器，其设备号是 0x021C
# 同理 /dev/at0 (2,8）指的是1.2MB A驱动器，其设备号是 0x0208。
#
# 下面 root_dev 定义在引导扇区 508，509字节处，指根文件系统所在设备号。
# 设备号 0x301 指定根文件系统设备是第1个硬盘的第1个分区。
# 如果根文件系统是在第2 个 Bochs 软盘上，那么该值应该为 0x021D，即(0x1D, 0x02）
#
# 当编译内核时，你可以在 Makefile 文件中另行指定你自己的值，内核映像
# 文件 Image 的创建程序 tools/build 会使用你指定的值来设置你的根文件系统所在设备号。
	mov	%cs:root_dev+0, %ax #root_dev中的值为ROOT_DEV = 0x301
	or	%ax, %ax
	jne	root_defined
	#seg cs
	mov	%cs:sectors+0, %bx
	mov	$0x0208, %ax		# /dev/ps0 - 1.2Mb
	cmp	$15, %bx
	je	root_defined
	mov	$0x021c, %ax		# /dev/PS0 - 1.44Mb
	cmp	$18, %bx
	je	root_defined
undef_root:
	jmp undef_root
root_defined:
	#seg cs
	mov	%ax, %cs:root_dev+0

# after that (everyting loaded), we jump to
# the setup-routine loaded directly after
# the bootblock:
# 所有的程序已被读取到指定内存段中, 还为根文件系统设置了默认设备(号),
# 现在跳转到0x90200（setup.s入口处指令），跳转后cs = 0x9020，ip = 0x0
	ljmp	$SETUPSEG, $0

# This routine loads the system at address 0x10000, making sure
# no 64kB boundaries are crossed. We try to load it as fast as
# possible, loading whole tracks whenever we can.
#
# in:	es - starting address segment (normally 0x1000)
#
sread:	.word 1+ SETUPLEN	# sectors read of current track
head:	.word 0			# current head
track:	.word 0			# current track

# 在阅读read_it相关子程序前, 先在BIOS层面粗略了解下启动盘-软盘的相关参数吧。
# BIOS读磁盘的中断调用是(int 13h)
# 以1.4Mb软盘为例,
# 有2个盘面             磁头号head(0开始)表示盘面,
# 每个面有80个磁道/柱面   磁道号track(0开始)表示磁道/柱面,
# 每个磁道有18个扇区     扇区号sread(1开始)表示 当前磁道已读扇区数(bootsect.s的1个扇区和setup.s的4个扇区)
# 每1个扇区有512字节空间,
# 即2 * 80 * 18 * 512 = 1.4Mb。
#
#
# 通过在子程序中不断组织磁头、磁道、扇区等参数给BIOS 13h才将数据读到内存中。
# 从启动盘中读取操作系统的大体逻辑是:
# for (track = 0; track < 80; ++track)
#    for (head = 0; head < 2; ++head)
#       while (未读完当前track和head下的扇区)
#           if ((未读扇区字节数 + 当前内存段已读字节数) < 内存段总字节数大小)
#               读 当前未读扇区内容到当前内存段
#           else
#               读取部分或全部未读扇区内容到当前内存段让当前内存段被读满
#               if (读满0x30000字节)
#                   读取结束
#               else
#                   往下一个内存段继续读取
# 当初将操作系统程序写入启动盘的时候也是按照这个逻辑写入的。
#

#
# reat_it,
# 读取启动盘-软盘上的0x30000字节的操作系统程序到[0x10000, 0x40000)内存段中。
# BIOS读取磁盘的中断调用(int 13h)会将所读内容保存到es:bx指向的内存中。
# es以64Kb边界对齐(es低16位为0), 若初始值不满足该条件则进入死循环。
# bx为段内偏移地址, 范围是0-64kb， 初值为0。
read_it:
	mov	%es, %ax
	test $0x0fff, %ax  #两个操作数进行"与"操作，结果全0时，ZF置1，否则ZF置0
die:
    jne die			# ZF#=0 则执行 es must be at 64kB boundary
	xor %bx, %bx	# bx is starting address within segment
rp_read:
	mov %es, %ax
	# %ax - $ENDSEG 结果等于0，ZF置1；若结果是负数，则表示$ENDSEG更大，减法发生借位，CF置1
 	cmp $ENDSEG, %ax	# have we loaded all yet?  %ax - $ENDSEG
	jb	ok1_read        # CF =1 则执行，$ENDSEG比ax大，说明还需要继续读数据
	ret
ok1_read:
	#seg cs
	mov	%cs:sectors+0, %ax #%cs:sectors+0 获取到磁盘扇区数
	sub	sread, %ax   # %ax = %ax - sread 磁道扇区数 - 已读扇区数 = 该磁道剩余的未读扇区数
	# 计算未读扇区数对应字节数(左移9位即乘512，一个扇区512字节)和已读扇区字节数之和是否超过64Kb
    # (16位寄存器发生进位则表示超过0xffff, 若刚好超过0xffff时16位寄存器值为0),
    # 若未超过则跳转ok2_read处读取, 若超过则计算当读满64Kb所需的扇区数。
	mov	%ax, %cx
	shl	$9, %cx
	add	%bx, %cx   # %cx = %cx + %bx
	# CF=0 没有溢出，则跳转，说明上面add结果，没有超过0xffff，也就是没有超过64Kb大小
	# 说明可以把剩余的扇区中的内容读到 es:bx地址
	jnc ok2_read
	# ZF = 1 则跳转，最近一次设置ZF 是在cmp $ENDSEG, %ax。若发生跳转，说明已经读完
	je 	ok2_read

	# 走到这里说明上面语句 add	%bx, %cx，结果超过64Kb，不能把剩余的扇区内容读到es:bx地址
	# 所以需要计算在64kb对齐的基础上，还能读入多少个扇区
    # 0-bx = -bx ，计算机中负数使用补码表示，也就是 0x10000(64Kb) - bx,即计算出还差多少字节满64Kb,
    # 右移9位，相当于除以512取整，表示最多能读入的扇区数
	xor %ax, %ax
	sub %bx, %ax
	shr $9, %ax
# ok2_read的主要功能
# 1、调用read_track从启动盘当前位置读取指定扇区数,
# 2、检查是否已经读完当前磁道的所有扇区,
# 3、若未读完当前磁道的所有扇区则跳转ok3_read处继续读取扇区内容到下一内存段
# 4、若已读完则检查当前磁头号是否为1, 不为1则跳转ok4_read处读取磁头号为1面上当前磁道上的所有扇区,
# 5、若磁头号已为1则增加磁道号已读取下一磁道上的扇区。
ok2_read:
	call 	read_track
	mov 	%ax, %cx
	add 	sread, %ax    # %ax = sread + %ax
	#seg cs
	# %ax - %cs:sectors+0  结果等于0，ZF置1；结果是负数，$ENDSEG更大，说明减法发生借位，CF置1
	cmp 	%cs:sectors+0, %ax
	# ZF = 0 则跳转，说明已读扇区数和磁道上的扇区数 不一致，该磁道上还有扇区没有读取
	jne 	ok3_read
	# 到这里说明该磁头的磁道的扇区已经读完，检查当前磁头号是否为1, 不为1则跳转ok4_read处读取磁头号为1面上
	mov 	$1, %ax
	sub 	head, %ax # %ax = %ax - head. 若head =1 则ax = 0，若head = 0，则ax = 1
	jne 	ok4_read
	# 如果该磁道的两个磁头的扇区都读取完了，就换磁道
	incw    track

# 更新磁头号
# 在ok2_read中 %ax 与 head值相反，在ok4_read中会改变head
ok4_read:
	mov	%ax, head
	xor	%ax, %ax

# 更新读取启动盘和内存段的相关参数后,
# 跳转rp_read继续读取启动盘。
ok3_read:
	mov	%ax, sread  # 重置当前磁道和磁头下的已读扇区数(0或已读扇区总数)
	shl	$9, %cx
	add	%cx, %bx    # 更新当前内存段 段内偏移
	jnc	rp_read     # 若16位寄存器未发生进位, 则表示未读满当前段, 则跳转rp_read继续读取
	mov	%es, %ax
	add	$0x10, %ah
	mov	%ax, %es    # 走到这里，说明当前内存段已满则让es指向下一个内存段
	xor	%bx, %bx    # 段内偏移重置为0
	jmp	rp_read     # 跳转rp_read处往当前内段读取启动盘上的内容

read_track:
	pusha
	pusha
	mov	$0xe2e, %ax 	# loading... message 2e = .
	mov	$7, %bx
 	int	$0x10
	popa

	mov	track, %dx
	mov	sread, %cx
	inc	%cx             # 已读扇区数+1，从下一个扇区开始读。从cl[5..0]扇区号对应扇区开始读取
	movb %dl, %ch       # cl[7..6] ch=磁道号
	mov	head, %dx
	movb %dl, %dh       # 将磁头号赋给dh
	and	$0x0100, %dx    # dl=驱动号, 0为软盘驱动号
	movb	$2, %ah     # ah=BIOS 13h功能号, 2h表示读操作

	push	%dx			# save for error dump
	push	%cx
	push	%bx
	push	%ax

	int	$0x13
	jc	bad_rt
	add	$8, %sp
	popa
	ret



bad_rt:	pushw	%ax				# save error code
	call	print_all			# ah = error, al = read

	xorb	%ah, %ah
	xorb	%dl, %dl
	int	$0x13

	addw	$10, %sp
	popa
	jmp	read_track

/*
 *	print_all is for debugging purposes.
 *	It will print out all of the registers.  The assumption is that this is
 *	called from a routine, with a stack frame like
 *	dx
 *	cx
 *	bx
 *	ax
 *	error
 *	ret <- sp
 *
*/

print_all:
	movw	$5, %cx		# error code + 4 registers
	movw	%sp, %bp

print_loop:
	pushw	%cx		# save count left
	call	print_nl	# nl for readability
	jae	no_reg		# see if register name is needed

	movw	$0xe05 + 0x41 - 1, %ax
	sub	%cl, %al
	int	$0x10

	movb	$0x58, %al 	# X
	int	$0x10

	movb	$0x3a, %al 	# :
	int	$0x10

no_reg:
	addw	$2, %bp		# next register
	call	print_hex	# print it
	popw	%cx
	loop	print_loop
	ret

print_nl:
	movw	$0xe0d, %ax	# CR
	int	$0x10
	movb	$0xa, %al	# LF
	int 	$0x10
	ret

/*
 *	print_hex is for debugging purposes, and prints the word
 *	pointed to by ss:bp in hexadecmial.
*/

print_hex:
	movw	$4, %cx		# 4 hex digits
	movw	(%bp), %dx	# load word into dx
print_digit:
	rol 	$4, %dx		# rotate so that lowest 4 bits are used
	movb	$0xe, %ah
	movb	%dl, %al	# mask off so we have only next nibble
	andb	$0xf, %al
	addb	$0x30, %al	# convert to 0 based digit, '0'
	cmpb	$0x39, %al	# check for overflow
	jbe	good_digit
	addb	$0x41 - 0x30 - 0xa, %al 	# 'A' - '0' - 0xa

good_digit:
	int	$0x10
	loop	print_digit
	ret


#/*
# * This procedure turns off the floppy drive motor, so
# * that we enter the kernel in a known state, and
# * don't have to worry about it later.
# */
kill_motor:
	push	%dx
	mov	$0x3f2, %dx
	mov	$0, %al
	outsb
	pop	%dx
	ret


sectors:
	.word 0

msg1:             # 开机调用BIOS中断显示的信息。共28（0x1c）个字符。
	.byte 13,10   # 回车、换行的ASCII码
	.ascii "Xue Xuan OS is booting ..."
#	.byte 13,10,13,10

# ORG是Origin的缩写：起始地址
.org 506
swap_dev:
	.word SWAP_DEV

# 在508的偏移地址处，保存2个字节，表示根文件系统所在设备的设备号
root_dev:
	.word ROOT_DEV

# 当启动盘最后两个字节为0xAA55时表明启动盘内容有效,
# BIOS读取引导程序到[0x7c00, 0x7e00)内存段后, 当启动盘有效时便跳转执行0x7c00。
boot_flag:
	.word 0xAA55
	
	.text
	endtext:
	.data
	enddata:
	.bss
	endbss:
